package com.liunian.discovery.impl;

import com.liunian.Constant;
import com.liunian.ServiceConfig;
import com.liunian.WrpcBootstrap;
import com.liunian.discovery.AbstractRegistry;
import com.liunian.exceptions.DiscoveryException;
import com.liunian.exceptions.NetworkException;
import com.liunian.utils.NetUtils;
import com.liunian.utils.zookeeper.ZookeeperNode;
import com.liunian.utils.zookeeper.ZookeeperUtils;
import com.liunian.watcher.UpAndDownWatcher;
import lombok.extern.slf4j.Slf4j;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
public class ZookeeperRegistry extends AbstractRegistry {

    private ZooKeeper zookeeper;

    public ZookeeperRegistry() {
        this.zookeeper = ZookeeperUtils.createZookeeper();
    }

    public ZookeeperRegistry(String connectString, int timeOut) {
        this.zookeeper = ZookeeperUtils.createZookeeper(connectString, timeOut);
    }

    @Override
    public void register(ServiceConfig<?> service) {
        // 服务名称的节点
        // 这个节点应该是一个持久节点
        String parentNode = Constant.BASE_PROVIDERS_PATH + "/" + service.getInterface().getName();
        // 建立服务节点，如果节点不存在就创建
        if (!ZookeeperUtils.exists(zookeeper, parentNode, null)) {
            ZookeeperNode zookeeperNode = new ZookeeperNode(parentNode, null);
            ZookeeperUtils.createNode(zookeeper, zookeeperNode, null, CreateMode.PERSISTENT);
        }

        // 建立分组节点
        parentNode = parentNode + "/" +service.getGroup();
        if (!ZookeeperUtils.exists(zookeeper, parentNode, null)) {
            ZookeeperNode zookeeperNode = new ZookeeperNode(parentNode, null);
            ZookeeperUtils.createNode(zookeeper, zookeeperNode, null, CreateMode.PERSISTENT);
        }


        // 创建本机的临时节点，ip:port，
        // 服务提供方的端口一般自己设定，但是我们还需要一个获取IP的方法
        // IP我们通常是需要一个局域网IP，不是127.0.0.1这样的，也不是IPv6这种
        // 像这种192.168.12.12
        // todo:
        String node = parentNode + "/" + NetUtils.getIp() + ":" + WrpcBootstrap.getInstance().getConfiguration().getPort();
        if (!ZookeeperUtils.exists(zookeeper, node, null)) {
            ZookeeperNode zookeeperNode = new ZookeeperNode(node, null);
            ZookeeperUtils.createNode(zookeeper, zookeeperNode, null, CreateMode.EPHEMERAL);
        }
        if (log.isDebugEnabled()) {
            log.debug("服务：{},已经被注册", service.getInterface().getName());
        }
    }

    /**
     * 注册中心的核心目的是什么？ ---- 拉取合适的服务列表。
     * @param ServiceName 服务的名称
     * @return 服务列表
     */
    @Override
    public List<InetSocketAddress> lookup(String ServiceName, String group) {
        // 1、找到服务对应的节点
        String serviceNode = Constant.BASE_PROVIDERS_PATH + "/" + ServiceName + "/" + group;
        // 2、从zk中获取他的子节点, 192.168.21.65:2181
        List<String> childern = ZookeeperUtils.getChildern(zookeeper, serviceNode, new UpAndDownWatcher());
        // 获取了所有的可用的服务列表
        List<InetSocketAddress> inetSocketAddresses = childern.stream().map(ipString -> {
            String[] ipAndPort = ipString.split(":");
            String ip = ipAndPort[0];
            int port = Integer.parseInt(ipAndPort[1]);
            return new InetSocketAddress(ip, port);
        }).collect(Collectors.toList());

        if (inetSocketAddresses.size() == 0) {
            throw new DiscoveryException("未发现任何可用的服务主机。");
        }
        // todo q: 我们每次调用相关方法的时候都需要去注册中心拉取服务列表吗？ ----> 本地缓存 + watcher
        //         我们如何合理的选择一个可用的服务，而不是只获取第一个  ---->  负载均衡策略
        return inetSocketAddresses;
    }
}
